/**
 * ...
 * @author waneck
 */

package asc.io;
import haxe.io.Bytes;
import js.Lib;

enum FileSeek {
	SeekBegin;
	SeekCur;
	SeekEnd;
}

class File 
{
	
	static inline function __init__():Void untyped
	{
		__js__("asc.io.FileHandle = File");
	}
	
	public static function getContent( path : String ):String 
	{
		var f = new FileHandle(path);
		f.open("text", "read");
		return f.read(f.length);
	}
	
	public static function getBytes( path : String ):Bytes
	{
		var f = new FileHandle(path);
		f.open("binary", "read");
		
		var b = new haxe.io.BytesOutput();
		for (i in 0...f.length)
			b.writeByte(f.readByte());
		
		return b.getBytes();
	}
	
	public static function read(path:String, binary:Bool):FileInput
	{
		var f:FileHandle = new FileHandle(path);
		if (!f.open((binary ? "binary" : "text"), "read"))
			throw haxe.io.Error.Custom("See application.onStatus()");
		return new FileInput(f);
	}
	
	public static function write(path:String, binary:Bool):FileOutput
	{
		var f:FileHandle = new FileHandle(path);
		if (!f.open((binary ? "binary" : "text"), "write"))
			throw haxe.io.Error.Custom("See application.onStatus()");
		return new FileOutput(f);
	}
	
	public static function append(path:String, binary:Bool):FileOutput
	{
		var f:FileHandle = new FileHandle(path);
		if (!f.open((binary ? "binary" : "text"), "append"))
			throw haxe.io.Error.Custom("See application.onStatus()");
		f.seek(f.length);
		return new FileOutput(f);
	}
	
	public static function copy(src:String, dst:String)
	{
		var f = new FileHandle(src);
		if (!f.copyTo(dst))
			throw haxe.io.Error.Custom("See application.onStatus()");
	}
}

extern class FileHandle
{
	/**
	 * Read only; a boolean value indicating whether a file can be appended (true) or not (false). 
	 * The property is undefined for closed files.
	 */
	var canAppend(default, null):Bool;
	
	/**
	 * Read-only; A boolean value indicating whether a file can be read (true) or not (false).
	 */
	var canRead(default, null):Bool;

	/**
	 * Read-only; A boolean value indicating whether a file was opened in "create" mode (true) or not
	 * (false). This property is undefined for closed files.
	 */
	var canReplace(default, null):Bool;
	
	/**
	 * Read only; a boolean value indicating whether a file can be written to (true) or not (false). 
	 */
	var canWrite(default, null):Bool;
	
	/**
	 * Read-only; a Date object containing the time the file was created.
	 */
	var creationTime(default, null):Date;
	
	/**
	 * Read-only; a boolean value indicating whether the file or directory exists (true) or not (false).
	 */
	var exists(default, null):Bool;
	
	/**
	 * Read-only; a boolean value indicating whether the file is a directory (true) or not (false).
	 */
	var isDirectory(default, null):Bool;
	
	/**
	 * Read-only; a boolean value indicating whether a file is a regular data file (true) or not (false). 
	 */
	var isFile(default, null):Bool;
	
	/**
	 * Read-only; a boolean value indicating whether the file has been successfully opened and is still
	 * open (true) or not (false). 
	 */
	var isOpen(default, null):Bool;
	
	/**
	 * Read-only; a Date object containing the time the file was last modified.
	 */
	var lastModified(default, null):Date;
	
	/**
	 * Read-only; for a directory, the number of files in the directory, not counting the current directory
	 * and parent directory entries; for a file, the number of bytes in the file.
	 */
	var length(default, null):Int;
	
	/**
	*  Read-only; the mode of an open file. It can be different from the mode parameter that was passed to the
	*  open() method for the file if you have repeating attributes (for example, "read, read") or if some
	*  attributes were ignored. If the file is closed, the property is undefined.
	*  
	**/
	var mode(default, null):String;
	
	/**
	*  Read-only; a string indicating the name of the file. If the File object was created with an
	*  invalid path, the value is an empty string.
	*  
	**/
	var name(default, null):String;
	
	/**
	*  The current offset in the file. This is the only property of the File class that can be set.
	*  Setting this property performs a seek operation on the file. The property is undefined for closed files. 
	*  
	**/
	var position(default, null):Int;
	
	/**
	 *  Read-only; a string specifying the type of data or encoding used when a file is opened.
	 *  The following strings are supported: "text", "utf8", and "binary". This property is undefined
	 *  for directories and closed files. If the file is opened in "text"  mode and UTF-8 BOM (Byte Order Mark)
	 *  is detected, the type  property is set to "utf8".
	 */
	var type(default, null):String;
	
	/**
	 * A File object that represents a directory has properties that represent the files contained
	 * in the directory. These properties have the same names as the files in the directory.
	 * 
	 * @param	fileName	file name to get inside a directory
	 * @return	File instance
	 */
	public inline function getFromDir(fileName:String):File
	{
		return untyped this[fileName];
	}
	
	/**
	 * Creates an instance of the File class.
	 * 
	 * @param	file	A string indicating the name of the file or directory. The name can contain
	 * 					only UTF-8 encoded characters; high byte values can be encoded by using the
	 * 					URI character-encoding scheme. The specified name is mapped to a system path 
	 * 					by using the mappings specified in the FileObject section of the Application.xml file.
	 * 
	 * Note that the physical file isn’t created on the hard disk until you call File.open().
	 */
	public function new(file:String):Void;
	
	/**
	 * Closes the file. This method is called automatically on an open File object when the object is out of scope.
	 * 
	 * @return	A boolean value indicating whether the file was closed successfully (true) or not (false).
	 * 			Returns false  if the file is not open.
	 */
	function close():Bool;

	/**
	 * Copies a file to a different location or copies it to the same location with a different filename.
	 * This method returns false if the source file doesn't exist or if the source file is a directory.
	 * When this method fails, it invokes the application.onStatus()  event handler to report errors.
	 * 
	 * @param	name	Specifies the name of the destination file. The name can contain only UTF-8 characters; 
	 * @return	A boolean value indicating whether the file is copied successfully (true) or not (false). 
	 */
	function copyTo(name:String):Bool;
	
	/**
	 * Returns a boolean value indicating whether the file pointer is at the end of file (true) or not (false).
	 * If the file is closed, the method returns true.
	 * @return
	 */
	function eof():Bool;
	
	/**
	 * Flushes the output buffers of a file. If the file is closed, the operation fails.
	 * When this method fails, it invokes the application.onStatus()  event handler to report errors.
	 * 
	 * @return	A boolean value indicating whether the flush operation was successful (true) or not (false).
	 */
	function flush():Bool;
	
	/**
	*  If the file is a directory, lists the files in the directory. Returns an array with an element for
	*  each file in the directory.
	*  
	*  @return An Array object.
	**/ 
	function list(filter:File->Bool):Array<File>;
	
	/**
	*  Creates a directory. The directory is created in the directory specified by fileObject. When this
	*  method fails, it invokes the application.onStatus()  event handler to report errors.
	*  
	*  The user or process owner that the server runs under in the operating system must have adequate write
	*  permissions or the call can fail.
	*  
	*  @param	newName		A string indicating the name of the new directory.
	*  						This name is relative to the current File object instance.
	*  @return 	A boolean value indicating success (true) or failure (false).
	**/
	function mkdir(newName:String):Bool;
	
	/**
	*  Opens a file so that you can read from it or write to it.
	*  First use the File constructor to create a File object and then call open() on that object.
	*  When the open() method fails, it invokes the application.onStatus()  event handler to report errors.
	*  
	*  @param	type	    A string indicating the encoding type for the file.:
	*  						"text", "binary", "utf8"
	*  @param	mode		A string indicating the mode in which to open the file. The following modes are
	*  						valid and can be combined (modes are case sensitive and multiple modes must be
	*  						separated by commas—for example, "read,write"; there is no default value): 
	*  						"read", "write", "readWrite", "append", "create"
	*  @return 	A boolean value indicating whether the file opened successfully (true) or not (false). 
	**/
	function open(type:String, mode:String):Bool;
	
	/**
	*  Reads the specified number of characters from a file and returns a string. If the file is opened in
	*  binary mode, the operation fails. When this method fails, it invokes the application.onStatus()  event
	*  handler to report errors.
	*  
	*  @param	numChars	A number specifying the number of characters to read. If numChars specifies more
	*  bytes than are left in the file, the method reads to the end of the file.
	*  @return 	A string
	**/
	function read(numChars:Int):String;
	
	/**
	*  Reads the file after the location of the file pointer and returns an Array object with an element for
	*  each line of the file. If the file opened in binary mode, the operation fails. When this method fails,
	*  it invokes the application.onStatus()  event handler to report errors.
	*  
	*  @return 	An Array
	**/
	function readAll():Array<String>;
	
	/**
	*  Reads the next byte from the file and returns the numeric value of the next byte or -1if the operation
	*  fails. If the file is not opened in binary mode, the operation fails.
	*  
	*  @return 	A number; either a positive integer or -1.
	**/
	function readByte():Int;
	
	/**
	*  Reads the next line from the file and returns it as a string. The line-separator characters (either
	*  \r\n on Windows or \n on Linux) are not included in the string. The character \r is skipped; \n
	*  determines the actual end of the line. If the file opened in binary mode, the operation fails.
	*  
	*  @return 	A String
	**/
	function readln():String;
	
	/**
	*  Removes the file or directory pointed to by the File object. When this method fails, it invokes the
	*  application.onStatus()  event handler to report errors.
	*  
	*  @param	recursive	A boolean value specifying whether to do a recursive removal of the directory and all its contents (true)
	*  
	*  @return 	A boolean value indicating whether the file or directory was removed successfully (true) or not (false).
	*  			Returns false if the file is open, the path points to a root folder, or the directory is not empty.
	**/
	function remove(?recursive:Bool = false):Bool;
	
	/**
	*  Moves or renames a file. If the file is open or the directory points to the root directory, the operation
	*  fails. When this method fails, it invokes the application.onStatus()  event handler to report errors.
	*  
	*  @param	name	The new name for the file or directory. The name can contain only UTF-8-encoded characters;
	*  					high byte values can be encoded by using the URI character-encoding scheme. The specified
	*  					name is mapped to a system path by using the mappings specified in the Application.xml file.
	*  					If the path is invalid or the destination file doesn’t exist, the operation fails.
	*  @return 	A boolean value indicating whether the file was successfully renamed or moved (true) or not (false). 
	**/
	function renameTo(name:String):Bool;
	
	
	/**
	 *  Skips a specified number of bytes and returns the new file position. This method can accept both positive
	 *  and negative parameters. 
	 *  
	 *  @param	numBytes	A number indicating the number of bytes to move the file pointer from the current position.
	 *  
	 *  @return 	If the operation is successful, returns the current position in the file; otherwise, returns -1.
	 *  			If the file is closed, the operation fails and calls application.onStatus()  to report a warning.
	 *  			The operation returns -1 when called on a directory.
	 */
	function seek(numBytes:Int):Int;
	
	/**
	*  Returns the path to the File object.
	**/
	function toString():String;
	
	/**
	 *  Writes data to a file. The write() method converts each parameter to a string and then writes it to the
	 *  file without separators. The file contents are buffered internally. The File.flush()  method writes
	 *  the buffer to the file on disk. When this method fails, it invokes the application.onStatus()  event
	 *  handler to report errors.
	 *  
	 *  @param	p0...p4	Parameters to write to the file.
	 *  @return 	A boolean value indicating whether the write operation was successful (true) or not (false).
	 */
	function write(p0:String, ?p1:String, ?p2:String, ?p3:String, ?p4:String):Bool;
	
	/**
	 *  Takes an Array object as a parameter and calls the File.writeln()  method on each element in the array.
	 *  The file contents are buffered internally. The File.flush()  method writes the buffer to the file on disk.
	 *  
	 *  @param	array	An Array object containing all the elements to write to the file.
	 *  @return 	A boolean value indicating whether the write operation was successful (true) or not (false).
	 */
	function writeAll(array:Array<String>):Bool;
	
	/**
	 *  Writes a byte to a file. The file contents are buffered internally. The File.flush()
	 *  method writes the buffer to the file on disk.
	 *  
	 *  @param 	number	A number to write.
	 *  @return 	A boolean value indicating whether the write operation was successful (true) or not (false).
	 */
	function writeByte(number:Int):Bool;
	
	/**
	 *  Writes data to a file and adds a platform-dependent end-of-line character after outputting the last parameter.
	 *  The file contents are buffered internally. The File.flush()  method writes the buffer to the file on disk.
	 *  
	 *  @param	p0...p4	Strings to write to the file. 
	 *  @return 	A boolean value indicating whether the write operation was successful (true) or not (false).
	 */
	function writeln(p0:String, ?p1:String, ?p2:String, ?p3:String, ?p4:String):Bool;
}